package com.mvc;


import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.mvc.annotation.*;
import com.mvc.handler.ParameterTypeHandler;
import com.mvc.handler.TypeHandlerManager;
import com.mvc.interceptor.*;
import com.spring.util.ClassUtils;
import com.spring.util.StringUtils;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 反射注册 servlet
 *
 * 存在 @Controller  的类里面扫描 存在  @RequestMapping 注解的方法  ,收集 url 集中起来
 *
 * 有这个注解，则进行动态代理，生成一个 servlet 子类
 */

@Slf4j
public class DispatchServlet extends HttpServlet {
	private MvcApplicationContext mvc;
	
	private static ObjectMapper objectMapper;  // json转换
	
	public static ObjectMapper getObjectMapper() {
		return objectMapper;
	}
	
	public DispatchServlet() {
		// 处理json 转换
		objectMapper = new ObjectMapper();
		JavaTimeModule module = new JavaTimeModule();
		
		// 加一个时间反序列化
		module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
		objectMapper.registerModule(module);
	}
	
	public void setMvc(MvcApplicationContext mvc) {
		this.mvc = mvc;
	}
	
	
	@Override
	protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doDispatch(req, resp);
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doDispatch(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doDispatch(req, resp);
	}
	
	
	/**
	 * 完成网络请求的分发工作，废弃了
	 *
	 * 增加 CORS 简单请求响应
	 *
	 * @param request
	 * @param response
	 */
	private void doDispatch2(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		// String uri = request.getRequestURI();
		// if (!mvc.hasUri(uri)) {
		// 	// 导航到 404 ，快速失败
		// 	response.setContentType("text/plain;charset=utf-8");
		// 	response.setStatus(404);
		// 	response.getWriter().write("404 not found : 请求的路径不存在哦!");
		// 	return;
		// }
		//
		// // 简单请求：不是OPTIONS ，但请求头携带  Origin 字段 ，其实不一定是简单请求，不跨域也可能发 Origin 字段
		// if (request.getHeader("Origin") != null) {
		// 	log.info("发现简单请求");
		// 	response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
		// 	response.setHeader("Access-Control-Allow-Credentials", "true");
		// 	response.setHeader("Access-Control-Allow-Headers", "Content-Type, Cookie");
		// }
		//
		// Parameter[] parameters = mvc.getParameter(uri);
		// Object[] paramValues = new Object[parameters.length];   //准备好参数数组
		// for (int i = 0; i < parameters.length; i++) {
		//
		// 	Parameter parameter = parameters[i];
		// 	String paramName = parameter.getName();
		//
		//
		// 	// 参数的封装处理
		// 	Class<?> paramType = parameter.getType();
		// 	if (paramType == HttpServletRequest.class) {
		// 		paramValues[i] = request;
		// 		continue;
		// 	}
		// 	if (paramType == HttpServletResponse.class) {
		// 		paramValues[i] = response;
		// 		continue;
		// 	}
		//
		// 	if (parameter.isAnnotationPresent(RequestBody.class)) {
		// 		// 有注解则从 请求体查找json 数据，反序列化生成 对象 ;  json 都是  {"key":"value","key":"value"} 形式
		// 		Object temp = objectMapper.readValue(request.getInputStream(), paramType);
		//
		// 		// @Valid 注解检查
		// 		if (parameter.isAnnotationPresent(Valid.class)) {
		// 			valid(temp);
		// 		}
		//
		// 		paramValues[i] = temp;
		//
		// 	} else {
		// 		// 默认是从 请求参数中获取数据  请求参数都是 key=value&key=value 形式
		// 		paramValues[i] = parseFromRequestParameter(request, parameter, paramName, paramType);
		// 	}
		// }
		//
		// // 开始调用目标方法
		// Pair<Method, Object> pair = mvc.getMethod(uri);
		// Method method = pair.getKey();
		//
		// Object ret = method.invoke(pair.getValue(), paramValues);
		//
		// // 最后检查下有没有 @ResponseBody 注解
		// if (method.isAnnotationPresent(ResponseBody.class)) {
		// 	response.setContentType("application/json;charset=utf-8");
		// 	String json = null;
		// 	if (ret instanceof String) {
		// 		json = (String) ret;
		// 	} else {
		// 		json = objectMapper.writeValueAsString(ret);
		// 	}
		//
		// 	// 根据json 响应状态，设置http响应
		// 	response.getWriter().write(json);
		// }
	}
	
	
	public void doDispatch(HttpServletRequest request, HttpServletResponse response) {
		String uri = request.getRequestURI();
		try {
			if (!mvc.hasUri(uri)) {
				// 导航到 404 ，快速失败
				ResponseUtil.notFound(response);
				return;
			}
			
			Exception dispatchException = null;
			HandlerExecutionChain handlerExecutionChain = mvc.getChain(uri);
			
			if (handlerExecutionChain == null) {
				// 直接执行目标方法
				doExecuteMethod(uri, request, response);
				return;
			}
			
			try {
				if (!handlerExecutionChain.applyPreHandler(request, response)) {
					return;      // 某个拦截器返回了false ,中断后续拦截器工作，认为响应已经被处理，所以这里直接return 掉，不管后续了
				}
				
				// 调用目标方法
				Object ret = doExecuteMethod(uri, request, response);
				
				// 后处理链
				handlerExecutionChain.applyPostHandler(request, response, ret);
			} catch (Exception e) {
				dispatchException = e;  // 异常等下丢给其他处理
			}
			handlerExecutionChain.applyAfterCompletion(request, response, dispatchException);
		} catch (Exception e) {
			try {
				// 导航至服务器错误
				ResponseUtil.ServerError(response);
			} catch (IOException ex) {
			}
			
		} finally {
			// 记得删除掉临时文件   Part.delete()
			try {
				if (!request.getParts().isEmpty()) {
					for (Part part : request.getParts()) {
						part.delete();
					}
				}
			} catch (IOException | ServletException e) {
			}
		}
	}
	
	private Object doExecuteMethod(String uri, HttpServletRequest request, HttpServletResponse response) throws Exception {
		Parameter[] parameters = mvc.getParameter(uri);
		Object[] paramValues = new Object[parameters.length];   //准备好参数数组
		for (int i = 0; i < parameters.length; i++) {
			Parameter parameter = parameters[i];
			String paramName = parameter.getName();
			
			// 参数的封装处理
			Class<?> paramType = parameter.getType();
			if (paramType == HttpServletRequest.class) {
				paramValues[i] = request;
				continue;
			}
			if (paramType == HttpServletResponse.class) {
				paramValues[i] = response;
				continue;
			}
			
			if (parameter.isAnnotationPresent(RequestBody.class)) {
				// 有注解则从 请求体查找json 数据，反序列化生成 对象 ;  json 都是  {"key":"value","key":"value"} 形式
				Object temp = objectMapper.readValue(request.getInputStream(), paramType);
				paramValues[i] = temp;
			} else {
				// 默认是从 请求参数中获取数据  请求参数都是 key=value&key=value 形式
				paramValues[i] = RequestParameterProcessor.parseFromRequestParameter(request, parameter, paramName, paramType);
			}
			
			// 最后还要检查下是否有 @Valid 注解，需要检查  ,只应该在 pojo 类型检查
			if (parameter.isAnnotationPresent(Valid.class)) {
				valid(paramValues[i]);
			}
		}
		
		Pair<Method, Object> pair = mvc.getMethod(uri);
		Method method = pair.getKey();
		Object ret = method.invoke(pair.getValue(), paramValues);
		return ret;
	}
	
	
	private static void valid(Object paramValue) throws Exception {
		Class<?> clazz = paramValue.getClass();
		
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (!needValid(field)) {
				continue;
			}
			
			String getterMethodName = StringUtils.getGetterMethodName(field.getName());
			Method getter = clazz.getMethod(getterMethodName);
			Object ret = getter.invoke(paramValue);
			
			if (ret == null) {
				throw new RuntimeException("注解验证失败:"+field.getName()+"提交参数值为null");
			}
			
			if (field.isAnnotationPresent(NotEmpty.class) && ret instanceof String && ((String) ret).isEmpty()) {
				throw new RuntimeException("注解验证失败"+field.getName()+"提交字符串为空");
			}
			
			if (field.isAnnotationPresent(Min.class) && ret instanceof Number) {
				Min min = field.getAnnotation(Min.class);
				if (min.value() > ((Number) ret).doubleValue()) {
					throw new RuntimeException("注解验证失败"+field.getName()+"小于最小值限制");
				}
			}
			if (field.isAnnotationPresent(Max.class) && ret instanceof Number) {
				Max max = field.getAnnotation(Max.class);
				if (max.value() < ((Number) ret).doubleValue()) {
					throw new RuntimeException("注解验证失败"+field.getName()+"大于最大值限制");
				}
			}
			if (field.isAnnotationPresent(Range.class) && ret instanceof Number) {
				Range range = field.getAnnotation(Range.class);
				double v = ((Number) ret).doubleValue();
				if (v < range.min() || v > range.max()) {
					throw new RuntimeException("注解验证失败"+field.getName()+"提交参数不在数值范围内");
				}
			}
		}
	}
	
	private static boolean needValid(Field field) {
		return field.isAnnotationPresent(NotNull.class) ||
				field.isAnnotationPresent(NotEmpty.class) ||
				field.isAnnotationPresent(Range.class) ||
				field.isAnnotationPresent(Min.class) ||
				field.isAnnotationPresent(Max.class);
	}
}
